﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using SaunaServer.View;
using SaunaServer.Model.Network;
using SaunaServer.Model.Database;

namespace SaunaServer.Controller.Communication
{
    public class CommunicationManager : ICommunicationManager
    {
        // Private variables
        private NetworkSetting networkSetting;
        private Thread threadCommunication;
        private ITcpFactory tcpFactory;
        private ITcpDaoGatewayServer tcpDao;
        private const string TCP_SSL = "TcpSsl";
        private IDBFactory dbFactory;
        private IDBDaoGateway dbDao;
        private const string DATABASE_DAO = "DatabaseDAO";
        CommunicationServerForm communicatinServerForm;

        // Constant
        private const string PROTOCOL_PREFIX = "$SAU";
        private const string PROTOCOL_LOGIN_REQUEST = "LI";
        private const string PROTOCOL_LOGIN_RESPONSE = "LR";
        private const string PROTOCOL_LOGIN_RESULT_OK = "OK";
        private const string PROTOCOL_LOGIN_RESULT_BAD = "BAD";
        private const string PROTOCOL_DELIMITER = ",";
        private const char PROTOCOL_DELIMITER_CHAR = ',';
        private const int PROTOCOL_COMMAND_LEVEL1 = 0;
        private const int PROTOCOL_COMMAND_LEVEL2 = 1;
        private const int PROTOCOL_COMMAND_LEVEL3 = 2;
        private const int PROTOCOL_COMMAND_LEVEL4 = 3;
        private string CONFIG_FILE_NAME = @"SaunaConfig.ini";
        private string MESSAGE_SERVER_START = "Server start";
        private string MESSAGE_SERVER_END = "Server end";
        
        /// <summary>
        /// Public Constructor
        /// </summary>
        public CommunicationManager(CommunicationServerForm communicatinServerForm)
        {
            // Store View module object of Communication Server Form 
            this.communicatinServerForm = communicatinServerForm; 
            
            // Read Sauna configuration file and create network setting
            networkSetting = ReadConfigFile();
            if (networkSetting == null)
            {
                // When Sauna configuration file does not exist, create network setting with default values
                networkSetting = new NetworkSetting();
            }
            
            // Create TCP DAO
            tcpFactory = new TcpFactoryImplementation();
            tcpDao = tcpFactory.Make(TCP_SSL, this, networkSetting);

            // Create Database DAO
            dbFactory = new DBFactoryImplementation();
            dbDao = dbFactory.Make(DATABASE_DAO, networkSetting);
        }

        /// <summary>
        /// Destructor
        /// </summary>
        ~CommunicationManager()
        {
            if (threadCommunication != null)
            {
                threadCommunication.Abort();
            }
        }

        /// <summary>
        /// Start Server Communication
        /// </summary>
        public void StartCommunication()
        {
            // Create communication thread
            threadCommunication = new Thread(new ParameterizedThreadStart(CommunicationHandlerThread));
            threadCommunication.IsBackground = true;

            //OutputLog("Communication Thread start");
            threadCommunication.Start();

            WriteMonitorMessage(MESSAGE_SERVER_START);        
        }

        /// <summary>
        /// End Server Communication
        /// </summary>
        public void EndCommunication()
        {
            tcpDao.EndCommunication();

            WriteMonitorMessage(MESSAGE_SERVER_END);
        }

        /// <summary>
        /// Communicatio Hander Thread
        /// </summary>
        /// <param name="obj">event object</param>
        private void CommunicationHandlerThread(object obj)
        {
            // Listen Communication from client
            tcpDao.ListenCommunication();
        }

        /// <summary>
        /// AuthenticateUser
        /// </summary>
        /// <param name="userId">User Id</param>
        /// <param name="password">Password</param>
        /// <param name="userFullName">User full name</param>
        public bool AuthenticateUser(string userId, string password, out string userFullName)
        {
            bool result = false;

            string userFullName1;
            result = dbDao.AuthenticateUser(userId, password, out userFullName1);
            userFullName = userFullName1;

            return result;
        }

        /// <summary>
        /// Create Download Log record
        /// </summary>
        /// <param name="userId">User Id</param>
        /// <param name="fileName">File Name</param>
        /// <param name="dateTime">Download date and time</param>
        public void CreateDownloadLogRecord(string userId, string fileName, string dateTime)
        {
            dbDao.CreateDownloadLogRecord(userId, fileName, dateTime);
        }

        /// <summary>
        /// Get Popular File Ranking
        /// <returns>Popular File Ranking List</returns>
        /// </summary>
        public string[,] GetPopularFileRanking()
        {
            return dbDao.GetPopularFileRanking();
        }

        /// <summary>
        /// Read Network setting from config file on current directory
        /// </summary>
        /// <returns>NetworkSetting</returns>
        public NetworkSetting ReadConfigFile()
        {
            return ReadConfigFileFromDirectory(CONFIG_FILE_NAME);
        }

        /// <summary>
        /// Read Network setting from config file on specified directory
        /// </summary>
        /// <param name="configDirFileName">config Directory and File Name</param>
        /// <returns>NetworkSetting</returns>
        public NetworkSetting ReadConfigFileFromDirectory(string configDirFileName)
        {

            NetworkSetting objNetworkSetting = null;
            try
            {
                using (Stream stream = new FileStream(configDirFileName, FileMode.Open, FileAccess.Read))
                {
                    IFormatter formatter = new BinaryFormatter();
                    objNetworkSetting = (NetworkSetting)formatter.Deserialize(stream);
                }
            }
            catch (FileNotFoundException ex)
            {
                // Write error.
                Console.WriteLine(ex);
            }
            return objNetworkSetting;
        }

        /// <summary>
        /// Write a message to monitor
        /// </summary>
        /// <param name="message">display message</param>
        public void WriteMonitorMessage(String message)
        {
            // null check for communicatinServerForm, because unit test requires null 
            if (communicatinServerForm != null)
            {
                communicatinServerForm.WriteMonitorMessage(message);
            }
        }
    

    }
}
